package top.cwk.dubbo.web.router;

import net.sf.json.JSONObject;
import top.cwk.dubbo.entity.serviceJson.ServiceJsonBean;
import top.cwk.dubbo.web.config.SpringContextUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName RouterToService
 * @Description RouterToService 匹配服务，反射调用
 * @Author 维康
 * @Date 2018/7/28 19:59
 * @Version 1.0
 **/
public class RouterToService {

    private static Map<Long,ServiceJsonBean> SERVICE_MAP = new HashMap<>();

    static public final boolean routerService(HttpServletRequest request, HttpServletResponse response) throws IOException,
                                                                                                                ClassNotFoundException,
                                                                                                                InvocationTargetException,
                                                                                                                IllegalAccessException,
                                                                                                                InstantiationException {
        SERVICE_MAP = (Map<Long, ServiceJsonBean>) request.getServletContext().getAttribute("SERVICE_MAP");
        String serviceIdStr = request.getParameter("serviceId");
        ServiceJsonBean serviceJsonBean = null;
        response.setCharacterEncoding("UTF-8");
        PrintWriter printWriter = response.getWriter();

        if(serviceIdStr!=null){
            Long serviceId = Long.valueOf(serviceIdStr);
            serviceJsonBean = SERVICE_MAP.get(serviceId);
            if(serviceJsonBean==null){
                printWriter.write("ERROR_101 : No interface was found based on serviceId="+serviceId+"!");
                return false;
            }
        }else{
            printWriter.write("ERROR_100 : The parameter [serviceId] should not be empty!");
            return false;
        }

        System.out.println(serviceJsonBean.toString());
        //反射获取请求的接口
        Class clz = Class.forName(serviceJsonBean.getServiceName());
        //根据class从spring容器获取对象
        Object obj = SpringContextUtil.getBean(clz);
        //反射获取该对象的所有方法
        Method[] methods = obj.getClass().getDeclaredMethods();
        String methodName = serviceJsonBean.getMethod();

        boolean findMethod = false;
        //遍历方法找到要执行的方法
        for (int i=0 ; i<methods.length ;i++){
            if(methodName.equals(methods[i].getName())){
                Method method = methods[i];
                Class[] parameterTypes = method.getParameterTypes();
                Object object = "";
                if(parameterTypes.length == 0){
                    //该方法为无参方法
                    object =  method.invoke(obj);
                }else{
                    if(parameterTypes.length == 1){
                        Class parameterType = parameterTypes[0];
                        //参数类型的名称
                        String simpleName = parameterType.getSimpleName();
                        //当参数为String/long/Integer/Date之类的时候 我们默认取参数名称为data
                        if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(simpleName)){
                            //这里默认一个参数的时候，默认字段为data 只能填 serviceId=100&data=xxx
                            //因为虚拟机编译后的字节码，参数名称会变成arg0..arg1之类的
                            String paramVal = request.getParameter("data");
                            if(paramVal!=null){
                                object = invokeSimpleParam(obj,method,simpleName,paramVal);
                            }else{
                                object = "ERROR_003 : The parameter [data] should not be empty!";
                            }
                        }else{
                            //服务当参数为一个自定义的对象时，反射获取对象，并将request的参数映射到对象的里的字段
                            Object arg = FieldUtil.setFieldValue(parameterType.getTypeName(),paramMapSet(request,parameterType.getTypeName()));
                            object =  method.invoke(obj ,arg);
                        }
                    }else{
                        System.err.println("暂不支持接口方法多参数调用，如果有多个参数，请封装为BO！");
                        object = "ERROR_002 : Method parameter too much! Only one parameter is allowed !";
                    }
                }
                printResult(object , printWriter);
                findMethod = true;
                break;
            }
        }
        if(!findMethod){
            String  result = "ERROR_001 :No matched method ["+serviceJsonBean.getMethod()+"] from service "+serviceJsonBean.getServiceName()+"!";
            printWriter.write(result);
        }
        return false;
    }


    /**
     * 参数为简单类型的时候
     */
    static private final Object invokeSimpleParam(Object invObject , Method method , String paramType , String paramValue)  {
        Object object = null;
        try {
            if(FieldTypeConstants.TYPE_STRING.equals(paramType)){
                object =  method.invoke(invObject , paramValue);
            }
            if(FieldTypeConstants.TYPE_LONG.equals(paramType)){
                object =  method.invoke(invObject , Long.parseLong(paramValue));
            }
            if(FieldTypeConstants.TYPE_INT.equals(paramType) || FieldTypeConstants.TYPE_INTEGER.equals(paramType) ){
                object =  method.invoke(invObject , Integer.parseInt(paramValue));
            }
            if(FieldTypeConstants.TYPE_BOOLEAN.equals(paramType)){
                object =  method.invoke(invObject , Boolean.parseBoolean(paramValue));
            }
            if(FieldTypeConstants.TYPE_DATE.equals(paramType)){
                object =  method.invoke(invObject , FieldUtil.parseDate(paramValue));
            }
            if(FieldTypeConstants.TYPE_DOUBLE.equals(paramType)){
                object =  method.invoke(invObject , Double.parseDouble(paramValue));
            }
            if(FieldTypeConstants.TYPE_FLOAT.equals(paramType)){
                object =  method.invoke(invObject , Float.parseFloat(paramValue));
            }
        } catch (IllegalAccessException e) {
            System.err.println("IllegalAccessException:"+e.getMessage());
            object = "ERROR_000 : IllegalAccessException:"+e.getMessage();
        } catch (InvocationTargetException e) {
            System.err.println("InvocationTargetException:"+e.getMessage());
            object = "ERROR_000 : InvocationTargetException:"+e.getMessage();
        }
        return object;
    }

    /**
     * 将request请求的参数封装为Map
     * @param request
     * @param paramTypeName
     * @return
     * @throws ClassNotFoundException
     */
    static private final Map<String,String> paramMapSet(HttpServletRequest request, String paramTypeName) throws ClassNotFoundException {
        Map<String,String> map = new HashMap<>();
        Field[] fields = Class.forName(paramTypeName).getDeclaredFields();
        for (int i=0 ; i<fields.length ;i++){
            String filedName = fields[i].getName();
            map.put(filedName,request.getParameter(filedName));
        }
        return map;
    }

    /**
     * 返回json格式的结果
     * @param object
     * @param printWriter
     */
    static private final void printResult(Object object , PrintWriter printWriter){
        String returnTypeName = object.getClass().getSimpleName();
        if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(returnTypeName)){
            String resStr = "";
            if(FieldTypeConstants.TYPE_DATE.equals(returnTypeName)){
                //时间格式需要特殊转换
                resStr =  FieldUtil.fmtDate((Date)object);
            }else {
                resStr = object.toString();
            }
            printWriter.write(resStr);
        }else{
            JSONObject jsonObject = JSONObject.fromObject(object);
            printWriter.write(jsonObject.toString(4));
        }
    }

}
